<?php
// 退款订单模型

namespace app\api\model;

use think\Model;
use think\facade\Cache;
use think\facade\Env;
use think\facade\Request;

class PaymentOrderRefund extends Model
{

    // 数据表名称,不包括表前缀
    protected $name = 'payment_order_refund';
    // 数据表主键
    protected $pk = 'order_id';
    // 时间字段取出后的默认时间格式
    protected $dateFormat = false;
    // Redis缓存驱动
    public $redis;

    protected function initialize()
    {
        parent::initialize();
        // redis连接缓存驱动
        $this->redis = Cache::store('redis');
        // 文件连接缓存驱动
        // $this->redis =Cache::store('file');
    }

    /**
     * 第三方退款通知相关操作
     * @access public
     * @param  string  $order_id         平台订单号
     * @param  string  $notify_param     通知参数
     * @param  string  $cash             金额
     * @param  string  $status           状态
     * @param  string  $other_order_id   第三方订单号
     * @param  string  $payment_company  第三方公司
     * @param  boolean $verify_rule      验证订单规则
     * @param  boolean $verify_sign      验证sign
     * @param  boolean $all_order_verify 内部绿色通道
     * @return mixed
     */
    public function notifyAbout($order_id = 0, $notify_param = '', $cash = 0, $status = 0, $other_order_id = 0, $payment_company = '', $verify_rule = true, $verify_sign = false, $all_order_verify = true)
    {
    }

    /**
     * 成功相关操作
     * @access public
     * @param  array  $order_arr       订单数组
     * @param  string $other_order_id  第三方订单号
     * @param  string $order_status    状态
     * @return mixed
     */
    public function paySuccess($order_arr = [], $other_order_id = 0, $order_status = 0)
    {
    }

    /**
     * 传化退款
     * @access public
     * @param  array  $parameters   创建订单参数
     * @param  array  $pay_config   支付配置
     * @return array
     */
    public function chuanhua($parameters = [], $pay_config = [])
    {
        try {
            $notify_url = $pay_config['other_two'] ? $pay_config['other_two'] : $pay_config['notify_url'];

            $arr['service_id'] = 'tf56pay.gateway.orderRefund';            // 接口服务ID
            $arr['appid'] = $pay_config['app_id'];                         // 商户注册时申请的应用编号，由运营申请分配
            $arr['tf_timestamp'] = date('YmdHis');                         // 时间戳格式：yyyyMMddHHmmss，如20141126161900
            $arr['sign_type'] = 'RSA';                                     // 商户生成签名字符串所使用的签名算法类型，目前支持MD5，RSA和RSA2，推荐使用RSA2或RSA
            $arr['terminal'] = 'H5';                                       // PC、Android、IOS、WP、EPOS、POS、ETC、PARK、MJ、SCANNER、CARD、H5
            $arr['version'] = '';                                          // 版本号 空

            // $arr['backurl'] = $notify_url;                              // 传化支付主动通知商户服务器交易结果,回调地址
            $arr['businessnumber'] = $parameters['old_order_id'];          // 原业务订单号
            $arr['refundbusinessnumber'] = $parameters['order_id'];        // 退款业务订单号, 建议订单号按规则：字母数字混合
            $arr['clientip'] = Request::instance()->ip();                  // 客户端请求IP
            $arr['refundamount'] = $parameters['cash'];                    // 退款金额，部分退款必传

            $arr = array_filter($arr);
            krsort($arr);
            $string = '';
            foreach ($arr as $k => $v) {
                $string .= $k . '=' . $v . '&';
            }

            $pkey = chunk_split($pay_config['key'], 64, "\n");
            $pkey = "-----BEGIN PRIVATE KEY-----\n" . $pkey . "-----END PRIVATE KEY-----\n";

            $cert = chunk_split($pay_config['other_public_key'], 64, "\n");
            $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";

            $header = [];
            $path = Env::get('root_path') . 'data' . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR;
            $infilename = $path . 'in' . $parameters['order_id'] . '.txt';
            $outfilename = $path . 'out' . $parameters['order_id'] . '.txt';
            file_put_contents($infilename, $string);
            openssl_pkcs7_sign(
                $infilename,
                $outfilename,
                $cert,
                $pkey,
                $header,
                PKCS7_BINARY | PKCS7_DETACHED
            );

            $signedData = explode("\n\n", file_get_contents($outfilename));

            $arr['tf_sign'] = str_replace("\n", '', $signedData[3]);

            $request_url = 'https://openapi.tf56.com/service/api';    // 请求地址
            $resdata = go_curl($request_url, http_build_query($arr), 'post', $pay_config['wait_second'], $header);
            $resarray = json_decode($resdata, true);
            $other_order_id = 0;
            $msg = '处理中';

            if (isset($resarray['biz_code']) && $resarray['biz_code'] == 'GPBIZ_00') {
                if (isset($resarray['data']) && strlen($resarray['data']) > 10) {
                    $data = json_decode($resarray['data'], true);
                    $other_order_id = isset($data['refundbusinessrecordnumber']) ? $data['refundbusinessrecordnumber'] : 100;
                }
                return ['code' => 200, 'other_order_id' => $other_order_id, 'msg' => $msg, 'return_param' => $resdata];
            }

            if (isset($resarray['biz_code']) && $resarray['biz_code'] == 'GPBIZ_032006') {
                $msg = isset($resarray['biz_msg']) ? $resarray['biz_msg'] : '处理中';
                return ['code' => 300, 'other_order_id' => $other_order_id, 'msg' => $msg, 'return_param' => $resdata];
            }

            return ['code' => 200, 'other_order_id' => $other_order_id, 'msg' => $msg, 'return_param' => $resdata];
        } catch (\Exception $e) {
            return ['code' => 200, 'other_order_id' => 1000, 'msg' => '处理中', 'return_param' => $e->getMessage()];
        }
    }

    /**
     * 传化退款，验证订单号，是不是已经支付
     * @access public
     * @param  string  $order_id    平台订单号
     * @param  array   $pay_config  支付配置
     * @param  array   $recharge    支付数据
     * @return boolean
     */
    public function chuanhuaVerify($order_id = 0, $pay_config = [], $other_param = [], $rule = false, $query_type = false)
    {
        $arr['service_id'] = 'tf56pay.gateway.orderQuery';    // 接口服务ID
        $arr['appid'] = $pay_config['app_id'];                // 商户注册时申请的应用编号，由运营申请分配
        $arr['tf_timestamp'] = date('YmdHis');                // 时间戳格式：yyyyMMddHHmmss，如20141126161900
        $arr['sign_type'] = 'RSA';                            // 商户生成签名字符串所使用的签名算法类型，目前支持MD5，RSA和RSA2，推荐使用RSA2或RSA
        $arr['terminal'] = 'H5';                              // PC、Android、IOS、WP、EPOS、POS、ETC、PARK、MJ、SCANNER、CARD、H5
        $arr['version'] = '';                                 // 版本号 空

        $arr['businessnumber'] = $order_id;                   // 需保证在商户端不重复，需保证在商户端不重复, 建议字母和数字的组合。

        $arr = array_filter($arr);
        krsort($arr);
        $string = '';
        foreach ($arr as $k => $v) {
            $string .= $k . '=' . $v . '&';
        }

        $pkey = chunk_split($pay_config['key'], 64, "\n");
        $pkey = "-----BEGIN PRIVATE KEY-----\n" . $pkey . "-----END PRIVATE KEY-----\n";

        $cert = chunk_split($pay_config['other_public_key'], 64, "\n");
        $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";

        $header = [];
        $path = Env::get('root_path') . 'data' . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR;
        $infilename = $path . 'in' . $order_id . '.txt';
        $outfilename = $path . 'out' . $order_id . '.txt';
        file_put_contents($infilename, $string);
        openssl_pkcs7_sign(
            $infilename,
            $outfilename,
            $cert,
            $pkey,
            $header,
            PKCS7_BINARY | PKCS7_DETACHED
        );

        $signedData = explode("\n\n", file_get_contents($outfilename));

        $arr['tf_sign'] = str_replace("\n", '', $signedData[3]);

        $request_url = 'https://openapi.tf56.com/service/api';    // 请求地址
        $resdata = go_curl($request_url, http_build_query($arr), 'post', $pay_config['wait_second'], $header);
        $resarray = json_decode($resdata, true);
        if (isset($resarray['biz_code']) && $resarray['biz_code'] == 'GPBIZ_030001') {
            return ['code' => 300, 'return_param' => substr($resdata, 0, 60000), 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];
        }
        return ['code' => 200, 'return_param' => substr($resdata, 0, 60000), 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];

        /**
          交易状态：
          已申请/处理中/成功/失败/关闭/已退款
         */
    }

    /**
     * 微信退款
     * @access public
     * @param  array  $parameters   创建订单参数
     * @param  array  $pay_config   支付配置
     * @return array
     */
    public function wxpay($parameters = [], $pay_config = [])
    {
        try {
            $notify_url = $pay_config['other_two'] ? $pay_config['other_two'] : $pay_config['notify_url'];

            // 微信v3版
            $notify_url = $notify_url . '/gs/' . $pay_config['name'];
            $mchid = $pay_config['merchant_id'];
            $serial_no = $pay_config['other_public_key'];
            $key = $pay_config['other_three'];
            $temp = chunk_split($key, 64, "\n");
            $key = "-----BEGIN PRIVATE KEY-----\n$temp-----END PRIVATE KEY-----\n";
            $mch_private_key = openssl_get_privatekey($key);
            $timestamp = time();
            $nonce = md5($timestamp);
            $http_method = 'POST';

            //$arr['appid'] = $pay_config['app_id'];                     // 应用ID
            //$arr['mchid'] = $pay_config['merchant_id'];                // 直连商户号
            $arr['out_trade_no'] = $parameters['old_order_id'];        // 原支付交易对应的商户订单号
            $arr['out_refund_no'] = $parameters['order_id'];           // 商户退款单号
            $arr['reason'] = '退款订单号' . $parameters['order_id'];    // 退款原因
            //$arr['notify_url'] = $notify_url;                          // 退款结果回调url
            $arr['amount'] = ['refund' => $parameters['cash'] * 100, 'total' => $parameters['old_cash'] * 100, 'currency' => 'CNY'];    // 订单总金额，单位为分  货币类型

            $url = 'https://api.mch.weixin.qq.com/v3/refund/domestic/refunds';

            $body = json_encode($arr);

            $url_parts = parse_url($url);
            $canonical_url = $url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : "");
            $message = $http_method . "\n" .
                $canonical_url . "\n" .
                $timestamp . "\n" .
                $nonce . "\n" .
                $body . "\n";

            openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
            $sign = base64_encode($raw_sign);

            $token = sprintf('WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $mchid, $nonce, $timestamp, $serial_no, $sign);

            $header = [
                'Accept: application/json',
                'User-Agent: */*',
                'Content-Type: application/json; charset=utf-8',
                'Authorization: ' . $token,
            ];

            $resdata = go_curl($url, $body, 'post', $pay_config['wait_second'], $header);
            $resarray = json_decode($resdata, true);
            if (isset($resarray['status']) && $resarray['status'] == 'PROCESSING') {
                $other_order_id = isset($resarray['refund_id']) ? $resarray['refund_id'] : 0;
                $msg = '处理中';
                return ['code' => 200, 'other_order_id' => $other_order_id, 'msg' => $msg, 'return_param' => $resdata];
            }
            if (isset($resarray['code']) && $resarray['code'] == 'INVALID_REQUEST') {
                $msg = isset($resarray['message']) ? $resarray['message'] : '失败';
                return ['code' => 300, 'other_order_id' => 0, 'msg' => $msg, 'return_param' => $resdata];
            }
            $other_order_id = isset($resarray['refund_id']) ? $resarray['refund_id'] : 0;
            $msg = '处理中';
            return ['code' => 200, 'other_order_id' => $other_order_id, 'msg' => $msg, 'return_param' => $resdata];
        } catch (\Exception $e) {
            return ['code' => 200, 'other_order_id' => 1000, 'msg' => '处理中', 'return_param' => $e->getMessage()];
        }
    }

    /**
     * 微信退款，验证订单号，是不是已经支付
     * @access public
     * @param  string  $order_id    平台订单号
     * @param  array   $pay_config  支付配置
     * @param  array   $recharge    支付数据
     * @return boolean
     */
    public function wxpayVerify($order_id = 0, $pay_config = [], $other_param = [], $rule = false, $query_type = false)
    {

        $out_refund_no = $order_id;    // 退款订单号
        $url = 'https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/' . $out_refund_no;

        $mchid = $pay_config['merchant_id'];
        $serial_no = $pay_config['other_public_key'];
        $key = $pay_config['other_three'];
        $temp = chunk_split($key, 64, "\n");
        $key = "-----BEGIN PRIVATE KEY-----\n$temp-----END PRIVATE KEY-----\n";
        $mch_private_key = openssl_get_privatekey($key);  // 私钥 
        $timestamp = time();  // 时间戳
        $nonce = md5($timestamp);  // 随机串
        $http_method = 'GET';

        $body = '';

        $url_parts = parse_url($url);
        $canonical_url = $url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : "");
        $message = $http_method . "\n" .
            $canonical_url . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $body . "\n";

        openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
        $sign = base64_encode($raw_sign);

        $token = sprintf('WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $mchid, $nonce, $timestamp, $serial_no, $sign);

        $header = [
            'Accept: application/json',
            'User-Agent: */*',
            'Content-Type: application/json; charset=utf-8',
            'Authorization: ' . $token,
        ];

        $resdata = go_curl($url, '', 'get', $pay_config['wait_second'], $header);
        $resarray = json_decode($resdata, true);
        if (isset($resarray['status'])   && $resarray['status'] == 'SUCCESS') {
            return ['code' => 200, 'return_param' => substr($resdata, 0, 60000), 'data' => json_encode($url, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];
        }
        return ['code' => 300, 'return_param' => substr($resdata, 0, 60000), 'data' => json_encode($url, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];

        /*
        SUCCESS：退款成功
        CLOSED：退款关闭
        PROCESSING：退款处理中
        ABNORMAL：退款异常
        示例值：SUCCESS
        */
    }

    /**
     * 支付宝退款
     * @access public
     * @param  array  $parameters   创建订单参数
     * @param  array  $pay_config   支付配置
     * @return array
     */
    public function zfbpay($parameters = [], $pay_config = [])
    {
        try {
            $pay_config['request_url'] = 'https://openapi.alipay.com/gateway.do';
            $notify_url = $pay_config['other_two'] ? $pay_config['other_two'] : $pay_config['notify_url'];

            include_once Env::get('extend_path') . 'alipaySdk/aop/AopClient.php';
            include_once Env::get('extend_path') . 'alipaySdk/aop/request/AlipayTradeRefundRequest.php';

            $arr['out_trade_no'] = $parameters['old_order_id'];
            $arr['refund_amount'] = $parameters['cash'];
            $arr['refund_reason'] = '退款订单号' . $parameters['order_id'];
            $arr['out_request_no'] = $parameters['order_id'];

            $aop = new \AopClient();
            $aop->gatewayUrl = $pay_config['request_url'];
            $aop->appId = $pay_config['app_id'];
            $aop->rsaPrivateKey = $pay_config['key'];
            $aop->alipayrsaPublicKey = $pay_config['other_public_key'];
            $aop->apiVersion = '1.0';
            $aop->signType = 'RSA2';
            $aop->postCharset = 'utf-8';
            $aop->format = 'json';

            $request = new \AlipayTradeRefundRequest();
            $request->setNotifyUrl($notify_url);
            $request->setBizContent(json_encode($arr));
            $result = $aop->execute($request);
            $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
            $resultCode = $result->$responseNode->code;
            $resdata = json_encode($result);
            if (!empty($resultCode) && $resultCode == 10000) {
                $other_order_id = $result->$responseNode->trade_no;
                $msg = $result->$responseNode->msg;
                return ['code' => 200, 'other_order_id' => $other_order_id, 'msg' => $msg, 'return_param' => $resdata];
            }
            if (!empty($resultCode) && $resultCode > 10000) {
                $msg = $result->$responseNode->sub_msg;
                return ['code' => 300, 'other_order_id' => 0, 'msg' => $msg, 'return_param' => $resdata];
            }
            return ['code' => 200, 'return_param' => $resdata];
        } catch (\Exception $e) {
            return ['code' => 200, 'other_order_id' => 1000, 'msg' => '处理中', 'return_param' => $e->getMessage()];
        }
    }

    /**
     * 支付宝退款，验证订单号，是不是已经支付
     * @access public
     * @param  string  $order_id    平台订单号
     * @param  array   $pay_config  支付配置
     * @param  array   $recharge    支付数据
     * @return boolean
     */
    public function zfbpayVerify($order_id = 0, $pay_config = [], $other_param = [], $rule = false, $query_type = false)
    {
        $pay_config['request_url'] = 'https://openapi.alipay.com/gateway.do';

        include_once Env::get('extend_path') . 'alipaySdk/aop/AopClient.php';
        include_once Env::get('extend_path') . 'alipaySdk/aop/request/AlipayTradeFastpayRefundQueryRequest.php';

        $arr['out_trade_no'] = $other_param['old_order_id'];
        $arr['out_request_no'] = $other_param['order_id'];

        $aop = new \AopClient();
        $aop->gatewayUrl = $pay_config['request_url'];
        $aop->appId = $pay_config['app_id'];
        $aop->rsaPrivateKey = $pay_config['key'];
        $aop->alipayrsaPublicKey = $pay_config['other_public_key'];
        $aop->apiVersion = '1.0';
        $aop->signType = 'RSA2';
        $aop->postCharset = 'utf-8';
        $aop->format = 'json';
        $request = new \AlipayTradeFastpayRefundQueryRequest();
        $request->setBizContent(json_encode($arr));
        $result = $aop->execute($request);
        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        $resdata = json_encode($result);
        if (!empty($resultCode) && $resultCode == 10000) {
            return ['code' => 200, 'return_param' => substr($resdata, 0, 60000), 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];
        }
        if (!empty($resultCode) && $resultCode > 10000) {
            return ['code' => 300, 'return_param' => substr($resdata, 0, 60000), 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];
        }
        return ['code' => 200, 'return_param' => substr($resdata, 0, 60000), 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];

        /**
          交易状态：
          WAIT_BUYER_PAY（交易创建，等待买家付款）
          TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）
          TRADE_SUCCESS（交易支付成功）
          TRADE_FINISHED（交易结束，不可退款）

          退款状态。枚举值：
          REFUND_SUCCESS 退款处理成功；
          未返回该字段表示退款请求未收到或者退款失败；
          注：如果退款查询发起时间早于退款时间，或者间隔退款发起时间太短，可能出现退款查询时还没处理成功，后面又处理成功的情况，建议商户在退款发起后间隔10秒以上再发起退款查询请求。
         */
    }
}
